package simulateOS.management.memory;

import javafx.application.Platform;
import javafx.collections.ObservableList;
import javafx.concurrent.Task;
import javafx.scene.Node;
import javafx.scene.paint.Color;
import javafx.scene.paint.Paint;
import javafx.scene.shape.Rectangle;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import simulateOS.controller.ProcessPageController;
import simulateOS.entity.PCB;
import simulateOS.entity.Partition;
import simulateOS.entity.Process;
import simulateOS.management.computerInterface.MemoryInterface;
import simulateOS.utils.CommonUtils;

import javax.annotation.Resource;
import java.util.*;
import java.util.concurrent.BlockingDeque;
import java.util.concurrent.LinkedBlockingDeque;

/**
 * Description：内存管理器
 * Date: 2022/9/26
 * Time: 23:58
 *
 * @Author SillyBaka
 **/
@Slf4j
@Service
public class MemoryManager implements MemoryInterface {
    /**
     * 系统区 PCB分配表
     * 0-空闲 1-已占用
     */
    private static final int[] SYSTEM_AREA_PCB;
    /**
     * 空闲PCB链表
     */
    private static final BlockingDeque<Integer> FREE_PCB_LIST;
    /**
     * 内存空间分配表
     */
    private static final LinkedList<Partition> ALLOCATED_MAP;
    /**
     * 空闲分区链表（按照起始下标递增排序）
     */
    private static final LinkedList<Partition> FREE_PARTITION_LIST;
    /**
     * 申请过内存的进程表，用于记录正在占有内存 或 正在申请内存的进程
     */
    private static final Set<Integer> ALLOCATED_PROCESS_MAP;
    /**
     * 给进程重复使用的10种不同颜色 哈哈
     */
    private static final Deque<Paint> COLOR_QUEUE;
    /**
     * 存放每个进程所占有的颜色
     */
    private static final Map<Integer,Paint> PROCESS_COLOR_MAP;

//    /**
//     * 阻塞队列 -- 等待分配内存空间的进程  -- 先进先出
//     */
//    private static final ArrayBlockingQueue<Process> WAITING_QUEUE_PROCESS;

    /**
     * 用户区
     * 0-空闲 1-已占用
     */
    private static final int[] USER_AREA;

    @Resource
    private ProcessPageController processPageController;

    static {
        SYSTEM_AREA_PCB = new int[10];

        FREE_PCB_LIST = new LinkedBlockingDeque<>();
        for (int i = 0; i < 10; i++) {
            FREE_PCB_LIST.add(i);
        }

        ALLOCATED_MAP = new LinkedList<>();

        FREE_PARTITION_LIST = new LinkedList<>();
        // 初始有一个占满用户区的大分区
        FREE_PARTITION_LIST.offer(new Partition(0,512,-1));

        USER_AREA = new int[512];

//        WAITING_QUEUE_PROCESS = new ArrayBlockingQueue<>(10);

        ALLOCATED_PROCESS_MAP = new HashSet<>();

        COLOR_QUEUE = new LinkedList<>();
        COLOR_QUEUE.add(Color.BLUE);
        COLOR_QUEUE.add(Color.BURLYWOOD);
        COLOR_QUEUE.add(Color.GOLD);
        COLOR_QUEUE.add(Color.ORANGE);
        COLOR_QUEUE.add(Color.TURQUOISE);
        COLOR_QUEUE.add(Color.LAWNGREEN);
        COLOR_QUEUE.add(Color.TOMATO);
        COLOR_QUEUE.add(Color.MOCCASIN);
        COLOR_QUEUE.add(Color.LIGHTSKYBLUE);
        COLOR_QUEUE.add(Color.LIGHTYELLOW);

        PROCESS_COLOR_MAP = new HashMap<>();
    }

    @Override
    public PCB getFreePCB() {
        // 申请空白进程控制块
        if(FREE_PCB_LIST.size() <= 0){
            log.info("暂无空白进程控制块...");

            return null;
        }

        Integer index = FREE_PCB_LIST.poll();
        SYSTEM_AREA_PCB[index] = 1;

        log.info("申请空白进程控制块成功，为第{}号PCB",index);

        PCB pcb = new PCB();
        pcb.setMemoryIndex(index);
        pcb.setPid(CommonUtils.getOnlyId());

        return pcb;
    }

    @Override
    public void getMemory(Process process) {

        int pid = process.getPcb().getPid();
        // 申请内存空间
        log.info("进程:{} 正在申请内存空间",pid);

        // 如果已申请空间 不可再申请
        if(ALLOCATED_PROCESS_MAP.contains(pid)){
            log.info("进程:{} 已申请内存空间，不可再次申请",pid);
            return;
        }

        boolean result = allocateMemory(process);

        ALLOCATED_PROCESS_MAP.add(pid);
        // 申请失败 进去等待队列
        if(!result){
            log.info("进程:{} 申请内存空间失败，已进入等待队列...",pid);
//            WAITING_QUEUE_PROCESS.offer(process);
        }
    }

    @Override
    public void releaseMemory(Process process) {

        int pid = process.getPcb().getPid();
        // 如果分配表中不存在 则不允许释放
        if(!ALLOCATED_PROCESS_MAP.contains(pid)){
            log.info("当前进程:{} 尚未申请内存，释放内存失败！",pid);
            return;
        }

        ListIterator<Partition> listIterator = ALLOCATED_MAP.listIterator();
        while (listIterator.hasNext()){
            Partition partition = listIterator.next();
            // 分区所属进程 和申请释放空间的是同一个进程
            if(partition.getPid() == pid){
                int index = partition.getStartIndex();
                int size = partition.getSize();
                // 创建新的空闲分区 并放入空闲区链表
                Partition freePart = new Partition(index, size, -1);
                FREE_PARTITION_LIST.offer(freePart);

                // 删除分配表中已分配分区
                for (int i = index; i < index+size ; i++) {
                    USER_AREA[i] = 0;
                }
                // 动态渲染视图
                refreshMemoryView(partition,false);

                listIterator.remove();

                // 释放成功 去除记录
                ALLOCATED_PROCESS_MAP.remove(process.getPcb().getPid());

                log.info("进程:{} 已释放内存",pid);

                // 释放PCB
                releasePCB(process.getPcb());

                break;
            }
        }
    }

    @Override
    public void releasePCB(PCB pcb) {
        int memoryIndex = pcb.getMemoryIndex();

        if(SYSTEM_AREA_PCB[memoryIndex] == 0){
            log.info("该空闲PCB已释放...释放失败");
        }else {
            SYSTEM_AREA_PCB[memoryIndex] = 0;
            FREE_PCB_LIST.add(memoryIndex);

            // 释放pid
            CommonUtils.releaseOnlyId(pcb.getPid());
            log.info("第{}号PCB已被释放",memoryIndex);
        }

    }


    /**
     * 线程任务类
     * 用于检查并处理 可执行文件分配内存等待队列
     */
//    class checkFileWaitingQueueTask implements Runnable{
//        @Override
//        public void run() {
//            while (true){
//                // 从阻塞队列中一个个取出 并检查是否有足够的内存空间
//                Process process;
//                try {
//                    process = WAITING_QUEUE_PROCESS.take();
//                    int pid = process.getPcb().getPid();
//                    int size = process.getSize();
//
//                    log.info("正在检查...现等待分配空间的进程：{}",pid);
//
//                    boolean result = allocateFile(process);
//                    // 分配失败 重新放入等待队列
//                    if(!result){
//                        log.info("进程:{} 再次申请内存失败，将重新进入等待队列...",pid);
//                        WAITING_QUEUE_PROCESS.offer(process);
//                    }
//                } catch (InterruptedException e) {
//                    e.printStackTrace();
//                    log.error("检查等待队列发送异常...");
//                }
//            }
//        }
//
//    }

    /**
     * 分配内存空间给目标进程
     * @param process 进程
     * @return false-失败 true-成功
     */
    private boolean allocateMemory(Process process) {
        int fileSize = process.getSize();
        int pid = process.getPcb().getPid();

        boolean result = false;
        ListIterator<Partition> listIterator = FREE_PARTITION_LIST.listIterator();
        // 检查空闲分区表
        while (listIterator.hasNext()){
            Partition partition = listIterator.next();

            // 分区足够分配
            if(partition.getSize() >= fileSize){
                // 分配给可执行文件后 将剩余的分区重新放入空闲区链表
                int startIndex = partition.getStartIndex();
                Partition allocated = new Partition(startIndex, fileSize,pid);
                // 放入已分配表中
                ALLOCATED_MAP.add(allocated);

                // 动态更新视图
                refreshMemoryView(allocated,true);

                // 标记用户区
                for (int j = startIndex; j < startIndex+ fileSize; j++) {
                    USER_AREA[j] = 1;
                }

                // 如果分区大小大于申请的大小 则把分区切开
                if(partition.getSize() > fileSize){
                    Partition freePart = new Partition(startIndex + fileSize, partition.getSize() - fileSize, -1);
                    // 删除原空闲区
                    listIterator.remove();
                    // 插入到当前空闲区链表中
                    listIterator.add(freePart);
                    // 否则直接分配 从空闲区中删除
                }else {
                    listIterator.remove();
                }
                log.info("进程：{} 申请内存空间成功！分配到的分区为:{}", pid,allocated);

                result = true;
                break;
            }
        }
        return result;
    }

    /**
     * 根据目标分区渲染内存视图
     * @param partition 指定分区
     * @param isAllocate 是否分配分区
     */
    public void refreshMemoryView(Partition partition,boolean isAllocate){
        // 提交线程任务
        Platform.runLater(new RefreshMemoryViewTask(partition,isAllocate));
    }

    /**
     * 刷新内存视图的异步任务
     */
    private class RefreshMemoryViewTask extends Task<Object>{

        private Partition partition;
        private boolean isAllocate;

        public RefreshMemoryViewTask(Partition partition, boolean isAllocate) {
            this.partition = partition;
            this.isAllocate = isAllocate;
        }

        @Override
        protected Object call() throws Exception {

            ObservableList<Node> children = processPageController.getMemoryPane().getChildren();
            int start = partition.getStartIndex();
            int end = start + partition.getSize();

            int pid = partition.getPid();

            // 若分配分区 则自动选颜色
            if(isAllocate){
                Paint color = COLOR_QUEUE.poll();
                PROCESS_COLOR_MAP.put(pid,color);
                for (int i = start; i < end; i++) {
                    children.set(i,new Rectangle(15, 15, color));
                }
            }else{
                Paint color = PROCESS_COLOR_MAP.remove(pid);
                COLOR_QUEUE.offer(color);
                for (int i = start; i < end; i++) {
                    children.set(i,new Rectangle(15, 15, Color.LIGHTGREY));
                }
            }
            return null;
        }
    }
}